import mysql.connector  # 导入MySQL连接器
import pandas as pd    # 导入pandas用于数据处理
from sqlalchemy import create_engine  # 导入SQLAlchemy引擎用于更好的数据库连接

class BilibiliDB:
    """B站数据库操作类
    这个类负责所有与数据库相关的操作，包括：
    - 连接数据库
    - 创建数据表
    - 保存爬取的数据
    - 查询数据等
    """

    def __init__(self, host="localhost", user="root", password="root", database="bilibili_analysis"):
        """
        初始化数据库连接
        参数说明：
        host: 数据库服务器地址，默认是本地主机
        user: 数据库用户名
        password: 数据库密码
        database: 要使用的数据库名称
        """
        self.host = host
        self.user = user
        self.password = password
        self.database = database
        self.conn = None          # 数据库连接对象
        self.cursor = None        # 数据库游标对象
        self.engine = None        # SQLAlchemy引擎对象
        self.connect()            # 建立连接

    def connect(self):
        """
        建立数据库连接
        这个方法会同时创建两种连接：
        1. 普通的MySQL连接（用于执行SQL语句）
        2. SQLAlchemy连接（用于pandas数据处理）
        """
        try:
            # 创建普通MySQL连接
            self.conn = mysql.connector.connect(
                host=self.host,
                user=self.user,
                password=self.password,
                database=self.database
            )
            self.cursor = self.conn.cursor()
            
            # 创建SQLAlchemy连接（用于pandas）
            self.engine = create_engine(
                f'mysql+mysqlconnector://{self.user}:{self.password}@{self.host}/{self.database}'
            )
        except Exception as e:
            print(f"数据库连接错误: {e}")

    def create_bilibili_videos_table(self):
        """
        创建B站视频数据表，并为video_url添加唯一索引
        """
        try:
            # 执行建表SQL语句
            self.cursor.execute("""
                CREATE TABLE IF NOT EXISTS bilibili_videos (
                    id BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT '主键ID',
                    title VARCHAR(255) COMMENT '视频标题',
                    view_count BIGINT COMMENT '播放量',
                    danmaku_count BIGINT COMMENT '弹幕数',
                    like_count BIGINT COMMENT '点赞数',
                    coin_count BIGINT COMMENT '投币数',
                    favorite_count BIGINT COMMENT '收藏数',
                    share_count BIGINT COMMENT '分享数',
                    up_name VARCHAR(100) COMMENT 'UP主名称',
                    up_id BIGINT COMMENT 'UP主ID',
                    video_category TEXT COMMENT '视频分区',
                    video_desc TEXT COMMENT '视频简介',
                    duration VARCHAR(10) COMMENT '视频时长',
                    publish_time DATETIME COMMENT '发布时间',
                    video_url VARCHAR(255) COMMENT '视频链接',
                    crawl_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '爬取时间',
                    UNIQUE KEY idx_video_url (video_url(255)) COMMENT '视频链接唯一索引'
                )
            """)
            self.conn.commit()
            print("表创建成功")
        except Exception as e:
            print(f"创建表失败: {e}")

    def check_duplicate_videos(self, video_urls):
        """
        检查视频是否已存在于数据库中
        
        参数：
        video_urls: list, 包含视频URL的列表
        
        返回：
        set: 已存在视频的URL集合
        """
        try:
            # 构建 IN 查询的占位符
            placeholders = ', '.join(['%s'] * len(video_urls))
            query = f"SELECT video_url FROM bilibili_videos WHERE video_url IN ({placeholders})"
            
            # 直接传递 video_urls 作为参数
            self.cursor.execute(query, video_urls)
            
            # 获取所有已存在的视频URL
            existing_urls = {row[0] for row in self.cursor.fetchall()}
            return existing_urls
            
        except Exception as e:
            print(f"检查重复视频时出错: {e}")
            return set()

    def save_to_db(self, results):
        """
        将爬取的视频数据保存到数据库，跳过重复的视频
        
        参数：
        results: 包含多个视频信息的列表，每个视频信息是一个字典
        """
        try:
            # 获取所有要插入的视频URL
            video_urls = [item['视频链接'] for item in results]
            
            # 检查重复的视频
            existing_urls = self.check_duplicate_videos(video_urls)
            
            # 过滤掉重复的视频数据
            new_results = [item for item in results if item['视频链接'] not in existing_urls]
            
            if not new_results:
                print("没有新的视频数据需要插入")
                return
                
            # 准备INSERT语句
            sql = """
            INSERT INTO bilibili_videos (
                title, view_count, danmaku_count, like_count, 
                coin_count, favorite_count, share_count, 
                up_name, up_id, video_category, video_desc,
                duration, publish_time, video_url, crawl_time
            ) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, NOW())
            """
            
            # 准备要插入的数据
            values = [
                (
                    item['标题'], item['播放量'], item['弹幕数'],
                    item['点赞数'], item['投币数'], item['收藏数'],
                    item['分享数'], item['UP主'], item['UP主ID'],
                    item['视频分区'], item['视频简介'], item['视频时长'],
                    item['发布时间'], item['视频链接']
                )
                for item in new_results
            ]

            # 批量插入数据
            self.cursor.executemany(sql, values)
            self.conn.commit()
            print(f"成功插入 {len(new_results)} 条新数据")
            if existing_urls:
                print(f"跳过 {len(existing_urls)} 条重复数据")

        except Exception as e:
            print(f"保存到数据库时出错: {e}")

    def get_videos_data(self):
        """
        从数据库获取所有视频数据
        
        返回值：
        pandas.DataFrame对象，包含所有视频的信息
        每一行是一个视频的数据，列名对应数据库中的字段名
        
        注意：
        这里临时使用publish_time作为crawl_time，
        因为早期版本可能没有crawl_time字段
        """
        try:
            # 准备SQL查询语句
            query = """
            SELECT 
                title,           -- 视频标题
                view_count,      -- 播放量
                danmaku_count,   -- 弹幕数
                like_count,      -- 点赞数
                coin_count,      -- 投币数
                favorite_count,  -- 收藏数
                share_count,     -- 分享数
                up_name,         -- UP主名称
                up_id,           -- UP主ID
                video_category as category,  -- 视频分区（重命名为category）
                video_desc,      -- 视频描述
                duration,        -- 视频时长
                publish_time,    -- 发布时间
                video_url,       -- 视频链接
                publish_time as crawl_time   -- 临时使用发布时间作为爬取时间
            FROM bilibili_videos
            """
            
            # 使用pandas读取SQL查询结果
            df = pd.read_sql(query, self.engine)
            return df
            
        except Exception as e:
            print(f"获取视频数据时出错: {e}")
            return pd.DataFrame()  # 发生错误时返回空的DataFrame

    def close(self):
        """
        关闭所有数据库连接
        包括：
        1. 普通MySQL连接的游标
        2. 普通MySQL连接
        3. SQLAlchemy引擎
        """
        if self.cursor:
            self.cursor.close()
        if self.conn:
            self.conn.close()
        if self.engine:
            self.engine.dispose()
        print("数据库连接已关闭")

    def truncate_table(self):
        """
        清空bilibili_videos表中的所有数据
        """
        try:
            self.cursor.execute("TRUNCATE TABLE bilibili_videos")
            self.conn.commit()
            print("数据表已清空")
        except Exception as e:
            print(f"清空数据表时出错: {e}")
